#include <algorithm>
#include "MyGraphics.hpp"
#include <assert.h>


static inline unsigned char FloatToUChar(float v) {
    v = std::clamp(v, 0.0f, 1.0f);
    return static_cast<unsigned char>(v * 255.0f + 0.5f);
}

Framebuffer::Framebuffer(int w, int h)
    : width(w), height(h)
{
    assert(width > 0 && height > 0);

    colorBuffer = new unsigned char[width * height * 4]; 
    depthBuffer = new float[width * height];

    ClearColor({0, 0, 0, 1});
    ClearDepth(1.0f);
}

Framebuffer::~Framebuffer()
{
    delete[] colorBuffer;
    delete[] depthBuffer;
}

void Framebuffer::ClearColor(const Vector4<float>& color)
{
    int numPixels = width * height;
    for (int i = 0; i < numPixels; ++i) {
        colorBuffer[i * 4 + 0] = FloatToUChar(color.x);
        colorBuffer[i * 4 + 1] = FloatToUChar(color.y);
        colorBuffer[i * 4 + 2] = FloatToUChar(color.z);
        colorBuffer[i * 4 + 3] = FloatToUChar(color.w);
    }
}

void Framebuffer::SetColorPixel(int col, int row, const Vector4<float> &color){
    assert(row >= 0 && row < height && col >= 0 && col < width);
    int idx = (row * width + col) * 4;
    colorBuffer[idx+0] = FloatToUChar(color.x);
    colorBuffer[idx+1] = FloatToUChar(color.y);
    colorBuffer[idx+2] = FloatToUChar(color.z);
    colorBuffer[idx+3] = FloatToUChar(color.w);
}

void Framebuffer::SetDepthPixel(int col, int row, float depth) {
    assert(row >= 0 && row < height && col >= 0 && col < width);
    depthBuffer[row * width + col] = depth;
}

void Framebuffer::ClearDepth(float depth)
{
    std::fill(depthBuffer, depthBuffer + width * height, depth);
}

void Framebuffer::DrawPixel(int col, int row, const Vector4<float> &color, float depth){
    assert(row >= 0 && row < height && col >= 0 && col < width);
    int idx = row * width + col;
    if(depth < depthBuffer[idx]){
        SetDepthPixel(col, row, depth);
        SetColorPixel(col, row, color);
    }
}